﻿#include "tools.h"
#include "Windows.h"

namespace VA
{

  Tools::Tools()
  {
  }

  Tools::~Tools()
  {
  }

  NAN_METHOD(Tools::ShowWindow)
  {

    if (info.Length() < 1)
    {

      Nan::ThrowError(std::string("args.error: " + std::to_string(info.Length())).c_str());
      info.GetReturnValue().Set(false);
      return;
    }
    HWND hwnd = (info[0]->IsUndefined() ? NULL : (HWND)Nan::To<int>(info[0]).FromJust());
    if (!hwnd || !::IsWindow(hwnd))
    {

      Nan::ThrowError(std::string("hwnd.error: " + std::to_string((int)hwnd)).c_str());
      info.GetReturnValue().Set(false);
      return;
    }
    info.GetReturnValue().Set(::ShowWindow(hwnd, SW_SHOW));
  }

  NAN_METHOD(Tools::HideWindow)
  {

    if (info.Length() < 1)
    {

      Nan::ThrowError(std::string("args.error: " + std::to_string(info.Length())).c_str());
      info.GetReturnValue().Set(false);
      return;
    }
    HWND hwnd = (info[0]->IsUndefined() ? NULL : (HWND)Nan::To<int>(info[0]).FromJust());
    if (!hwnd || !::IsWindow(hwnd))
    {

      Nan::ThrowError(std::string("hwnd.error: " + std::to_string((int)hwnd)).c_str());
      info.GetReturnValue().Set(false);
      return;
    }
    info.GetReturnValue().Set(::ShowWindow(hwnd, SW_HIDE));
  }

  NAN_METHOD(Tools::toHwnd)
  {

    if (info.Length() < 1)
    {

      Nan::ThrowError(std::string("args.error: " + std::to_string(info.Length())).c_str());
      info.GetReturnValue().Set(false);
      return;
    }
    if (!info[0]->IsObject())
    {

      Nan::ThrowError("Expected first argument to be a window handle buffer");
      info.GetReturnValue().Set(false);
      return;
    }
    auto isolate = info.GetIsolate();
    if (!isolate)
    {

      Nan::ThrowError("Expected isolate");
      info.GetReturnValue().Set(false);
      return;
    }
    auto context = isolate->GetCurrentContext();
    if (context.IsEmpty())
    {

      Nan::ThrowError("Expected context");
      info.GetReturnValue().Set(false);
      return;
    }
    unsigned char *windowHandleBuffer = (unsigned char *)node::Buffer::Data(info[0]->ToObject(context).ToLocalChecked());
    if (!windowHandleBuffer)
    {

      Nan::ThrowError("Expected windowHandleBuffer");
      info.GetReturnValue().Set(false);
      return;
    }
    LONG_PTR handle = *reinterpret_cast<LONG_PTR *>(windowHandleBuffer);
    if (!handle)
    {

      Nan::ThrowError("Expected handle");
      info.GetReturnValue().Set(false);
      return;
    }
    HWND hwnd = (HWND)(LONG_PTR)handle;
    if (!hwnd || !::IsWindow(hwnd))
    {

      Nan::ThrowError(std::string("hwnd.error: " + std::to_string((int)hwnd)).c_str());
      info.GetReturnValue().Set(false);
      return;
    }
    info.GetReturnValue().Set((int)hwnd);
  }

  NAN_METHOD(Tools::MoveWindow)
  {

    if (info.Length() < 5)
    {
      Nan::ThrowError(std::string("args.error: " + std::to_string(info.Length())).c_str());
      info.GetReturnValue().Set(false);
      return;
    }
    HWND hwnd = (info[0]->IsUndefined() ? NULL : (HWND)Nan::To<int>(info[0]).FromJust());
    if (!hwnd || !::IsWindow(hwnd))
    {

      Nan::ThrowError(std::string("hwnd.error: " + std::to_string((int)hwnd)).c_str());
      info.GetReturnValue().Set(false);
      return;
    }
    int x = (info[1]->IsUndefined() ? NULL : Nan::To<int>(info[1]).FromJust());
    /*
      if (!x || x < 0) {

          Nan::ThrowError(std::string("x.error: " + std::to_string(x)).c_str());
          info.GetReturnValue().Set(false);
          return;
      }
      */
    int y = (info[2]->IsUndefined() ? 0 : Nan::To<int>(info[2]).FromJust());
    /*
      if (!y || y <= 0) {

          Nan::ThrowError(std::string("y.error: " + std::to_string(x)).c_str());
          info.GetReturnValue().Set(false);
          return;
      }
      */
    int width = (info[3]->IsUndefined() ? 0 : Nan::To<int>(info[3]).FromJust());
    if (!width || width <= 0)
    {

      Nan::ThrowError(std::string("width.error: " + std::to_string(x)).c_str());
      info.GetReturnValue().Set(false);
      return;
    }
    int height = (info[4]->IsUndefined() ? 0 : Nan::To<int>(info[4]).FromJust());
    if (!height || height <= 0)
    {

      Nan::ThrowError(std::string("height.error: " + std::to_string(x)).c_str());
      info.GetReturnValue().Set(false);
      return;
    }
    info.GetReturnValue().Set(::MoveWindow(hwnd, x, y, width, height, true));
  }

  typedef struct
  {
    HWND hwndWindow;
    DWORD dwProcessID;
  } EnumWindowsArg;

  BOOL CALLBACK EnumVMWindowsProc(HWND hwnd, LPARAM lParam)
  {
    EnumWindowsArg *pArg = (EnumWindowsArg *)lParam;
    DWORD dwProcessID = 0;
    ::GetWindowThreadProcessId(hwnd, &dwProcessID);
    if (dwProcessID == pArg->dwProcessID)
    {

      CHAR szClassName[MAX_PATH] = {0};
      CHAR szTitle[MAX_PATH] = {0};
      if (::GetClassName(hwnd, szClassName, MAX_PATH) > 0 &&
          ::strcmp(szClassName, "RenderWindow") == 0 &&
          ::GetWindowText(hwnd, szTitle, MAX_PATH) > 0 &&
          ::strcmp(szTitle, "TheRender") == 0)
      {
        pArg->hwndWindow = hwnd;
        return FALSE;
      }
    }
    return TRUE;
  }

  NAN_METHOD(Tools::toVMrenderhwnd)
  {

    if (info.Length() < 1)
    {
      Nan::ThrowError(std::string("args.error: " + std::to_string(info.Length())).c_str());
      info.GetReturnValue().Set(0);
      return;
    }
    int pid = (info[0]->IsUndefined() ? 0 : Nan::To<int>(info[0]).FromJust());
    if (!pid || pid <= 0)
    {

      Nan::ThrowError(std::string("pid.error: " + std::to_string(pid)).c_str());
      info.GetReturnValue().Set(0);
      return;
    }
    EnumWindowsArg ewa;
    ewa.dwProcessID = pid;
    ewa.hwndWindow = NULL;
    ::EnumWindows(EnumVMWindowsProc, (LPARAM)&ewa);
    if (ewa.hwndWindow)
    {
      info.GetReturnValue().Set((int)ewa.hwndWindow);
    }
    else
    {
      Nan::ThrowError(std::string("to hwnd.error: " + std::to_string(pid)).c_str());
      info.GetReturnValue().Set(0);
    }
  }

  NAN_METHOD(Tools::Test)
  {
  }
}
